/*--------------------------------------------------------------------------------------------------
Yatak Operation System Project Copyright 2012-2013
Author : Ilya Churaev
Started : 20 Apr 2012		Updated : 28 Feb 2013
File Name : kernel.c		Purpose : operation system is starting work
--------------------------------------------------------------------------------------------------*/
#include <svga/vga_modes.h>
#include <yatak/version.h>
#include <yatak/devices.h>
#include <yatak/paging.h>
#include <yatak/yatak.h>
#include <yatak/kheap.h>
#include <yatak/debug.h>
#include <net/netdev.h>
#include <yatak/idt.h>
#include <yatak/isr.h>
#include <yatak/gdt.h>
#include <multiboot.h>
#include <fs/initrd.h>
#include <keyboard.h>
#include <syscalls.h>
#include <hardware.h>
#include <net/net.h>
#include <screen.h>
#include <shell.h>
#include <cpuid.h>
#include <users.h>
#include <timer.h>
#include <sched.h>
#include <list.h>
#include <pci.h>
#include <log.h>
#include <fs.h>

#ifdef DEBUG
	#define DEBUG_MAIN
#endif

#ifdef DEBUG_MAIN
	#define dprintf kprintf
	#define DBGPRINTF(s) outportb(0xe9, s);
#else
	#define dprintf
	#define DBGPRINTF(s)
#endif

struct system_info current_system = {0};
extern bool showtime;
extern uint32 placement_address;
uint32 initial_esp;

extern fs_node_t *fs_root;
// This is variable for graphical mode
volatile int graphical_mode = 0;

// Initialisating functions
void Start0 (unsigned long magic, unsigned long addr, uint32 initial_stack);
void ReadMultiboot (multiboot_info_t *mbi);
void Start2 (unsigned int mem_size, uint32 initrd_location);

// Setup bit for blink
extern void ToggleIntensityBlinkBit ();

// Start window
void StartWindow ();

// test function
int test_memory();
int test_initrd();
void hello_world(char * a);
void hello_world2(char * a);

extern int detect_cpu_speed ();
void Start0 (unsigned long magic, unsigned long addr, uint32 initial_stack)
{
	initial_esp = initial_stack;
	// Установка вывода на экран
	init_video();
	// Настройка Global Descriptor Table
	init_gdt();
	// Настройка Interrupt Descriptor Table
	init_idt();
	
	// Initialise the PIT to 100Hz
	init_timer(100);
	
	// Initialise ACPI
	init_acpi();
	
	// Detect CPU
	detect_cpu (&(current_system.cpuinfo));
	//int cpu_sp = detect_cpu_speed ();
	//kprintf ("\n%d\n",cpu_sp);
	//for (;;);
	// Будет включать все ядра процессора
	SmpPrepare();
	
	if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
	{
		// If magic != MULTIBOOT_BOOTLOADER_MAGIC
		// I start init_paging witch 16 mb...
		// And I don't mount initrd
		kprintf ("Invalid magic number: 0x%x\n", (unsigned) magic);
		//Start2 (16*1024*1024,0);
		//for (;;);
		return;
	}
	
	multiboot_info_t *mbi;
	// Set MBI to the address of the Multiboot information structure.
	mbi = (multiboot_info_t *) addr;
	
	placement_address = *(uint32*)(mbi->mods_addr+4);
	ReadMultiboot (mbi);
	for (;;);
}

void ReadMultiboot (multiboot_info_t *mbi)
{
	uint32 initrd_location = 0;
	uint32 initrd_end = 0;
	initrd_location = *((uint32*)mbi->mods_addr);
	initrd_end = *(uint32*)(mbi->mods_addr+4);
	// Print out the flags.
	kprintf ("flags = 0x%x\n", (unsigned) mbi->flags);
	
	// Are mem_* valid?
	if (CHECK_FLAG (mbi->flags, 0))
		kprintf ("mem_lower = %d KB, mem_upper = %d KB\nsize memory = %d MB\n",(unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper,(unsigned) mbi->mem_upper/1024);
	// Is boot_device valid?
	if (CHECK_FLAG (mbi->flags, 1))
		kprintf ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);
		
	// Is the command line passed?
	if (CHECK_FLAG (mbi->flags, 2))
		kprintf ("cmdline = %s\n", (char *) mbi->cmdline);
	
	// Are mods_* valid?
	if (CHECK_FLAG (mbi->flags, 3))
	{
		multiboot_module_t *mod;
		int i;
		kprintf ("mods_count = %d, mods_addr = 0x%x\n",(int) mbi->mods_count, (int) mbi->mods_addr);
		placement_address = *(uint32*)(mbi->mods_addr+4);
		for (i = 0, mod = (multiboot_module_t *) mbi->mods_addr; i < mbi->mods_count; i++, mod++)
		{
			kprintf (" mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n",(unsigned)mod->mod_start,(unsigned)mod->mod_end,(char *)mod->cmdline);
		}
	}
	/* Bits 4 and 5 are mutually exclusive! */
	if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
	{
		kprintf ("Both bits 4 and 5 are set.\n");
		return;
	}
	
	/* Is the symbol table of a.out valid? */
	if (CHECK_FLAG (mbi->flags, 4))
	{
		multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym);
		
		kprintf ("multiboot_aout_symbol_table: tabsize = 0x%0x, "
				"strsize = 0x%x, addr = 0x%x\n",
				(unsigned) multiboot_aout_sym->tabsize,
				(unsigned) multiboot_aout_sym->strsize,
				(unsigned) multiboot_aout_sym->addr);
	}
	
	/* Is the section header table of ELF valid? */
	if (CHECK_FLAG (mbi->flags, 5))
	{
		multiboot_elf_section_header_table_t *multiboot_elf_sec = &(mbi->u.elf_sec);
		
		kprintf ("multiboot_elf_sec: num = %u, size = 0x%x,"
				" addr = 0x%x, shndx = 0x%x\n",
				(unsigned) multiboot_elf_sec->num, (unsigned) multiboot_elf_sec->size,
				(unsigned) multiboot_elf_sec->addr, (unsigned) multiboot_elf_sec->shndx);
	}
	
	/* Are mmap_* valid? */
	if (CHECK_FLAG (mbi->flags, 6))
	{
		multiboot_memory_map_t *mmap;
		
		kprintf ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
                   (unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
		for (mmap = (multiboot_memory_map_t *) mbi->mmap_addr;
							(unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
							mmap = (multiboot_memory_map_t *) ((unsigned long) mmap + mmap->size + sizeof (mmap->size)))
		kprintf (" size = 0x%x, base_addr = 0x%x%x,"
			" length = 0x%x%x, type = 0x%x\n",
			(unsigned) mmap->size,
			mmap->addr >> 32,
			mmap->addr & 0xffffffff,
			mmap->len >> 32,
			mmap->len & 0xffffffff,
			(unsigned) mmap->type);
	}
	
	uint32 memsz = ((mbi->mem_lower + mbi->mem_upper) * 1024); //Bytes
	Start2 (memsz, initrd_location);
	for (;;);
}

void Start2 (unsigned int mem_size, uint32 initrd_location)
{
	//Initialise paging with the memory amount reported by GRUB
	kprintf("Paging try to initialization with %dMB of memory...\n",(mem_size/1024/1024));
	init_paging(mem_size*1024);
	current_system.memory = mem_size;
	
	uint32 malloc_test = test_memory ();
	if(malloc_test != 0)
	{
		dprintf("[Start2] MMU working as expected:\n[Start2] \tMemory test assigned 100 bytes at 0x%x...\n",malloc_test);
	}
	else
		dprintf("[Start2] MMU failed test...\n");
	
	// Start multitasking.
	init_tasking();
     
	//set up syscalls so our applications can do stuff
	//syscalls are set up to IRQ11(int 43)
	init_syscalls();
	
	sti();
	
	if (initrd_location)
	{
		fs_root = init_initrd(initrd_location);
		SetCurrentNodeDir (fs_root);
		MessageFoS ("RAMFS initialised...\t\t\t\t\t\t",true);
	}
	else
	{
		MessageFoS ("RAMFS initialised...\t\t\t\t\t\t",false);
	}
	
	// Test initrd
	//if (!test_initrd())
	//	dprintf ("[Start2] Test initrd...OK\n");
	//else
	//	dprintf ("[Start2] Test initrd...FAIL\n");
	
	kprintf("Initialising PCI bus...\n");
	init_pci(&current_system);
	
	static int mouse;
	mouse = detect_ps2_mouse();
	if(mouse)
	{
		MessageFoS ("PS/2 mouse detecting...\t\t\t\t\t\t",true);
		init_ps2_mouse();
		MessageFoS ("PS/2 mouse initialised...\t\t\t\t\t",true);
	}
	else
	{
		kprintf("No PS/2 mouse found. Looking for alternative...\n");
		MessageFoS ("PS/2 mouse detecting...\t\t\t\t\t\t",false);
		//Scan for non-PS/2 mouse here
	}
	
	// Setup keyboard
	init_keyboard();
	
	// Init Network Protocol Manager
	init_net();
	// Initialize Net Packets Buffering
	init_net_buff();
	// Initialize Network Devices
	init_el3();
	// Initialize ARP Protocol
	init_arp();
	// Initialize IP Protocol
	init_ip();
	// Initialize UDP Protocol
	init_udp();
	// Initialize TCP Protocol
	init_tcp();
	// Initialize Socket interface
	init_socket();
	//ide_initialize(0x1F0, 0x3F4, 0x170, 0x374, 0x000);
	ToggleIntensityBlinkBit ();
	
	//print_log("Init_log");
	//print_log("System_log");
	StartWindow ();
	// Login
	Enter_in_System ();
	showtime = true;
	init_user ();
	//set_vga_mode((int*)VESA_MODE_824);
	//switch_to_user_mode();
	for (;;);
}

int test_memory ()
{
	uint32 * malloc_test = kmalloc(100);
	if(malloc_test != 0)
	{
		uint32 tmp = malloc_test;
		kfree((void*)malloc_test);
		return tmp;
	}
	return 0;
}

int test_initrd ()
{
	// Список содержимого директория
	int i = 0;
	struct dirent *node = 0;
	if ((node = readdir_fs(fs_root, i)) == 0)
		return -1;
	while ((node = readdir_fs(fs_root, i)) != 0)
	{
		dprintf("[test_initrd] Found file %s...\n",node->name);
		fs_node_t *fsnode = finddir_fs(fs_root, node->name);

		if ((fsnode->flags&0x7) == FS_DIRECTORY)
			dprintf("[test_initrd] \t%s is (directory)\n", node->name);
		else
		{
			dprintf("[test_initrd] \tcontents of %s is: \"",node->name);
			char buf[256];
			read_fs(fsnode, 0, 256, buf);
			dprintf("[test_initrd] %s\"\n",buf);
		}
		i++;
	}
	return 0;
}


void hello_world(char * a)
{
	unsigned int flags;
	save_flags (flags);
	kprintf("%s\n",a);
	restore_flags(flags);
}
void hello_world2(char * a)
{
	unsigned int flags;
	save_flags (flags);
	kprintf("%s\n",a);
	restore_flags(flags);
	for (;;);
}

void StartWindow ()
{
	unsigned int flags;
	save_flags (flags);
	cli ();
	settextcolor(__BLACK, __WHITE);
	cls ();
	// A
	paint_line (9,17,42,7,-1);
	paint_line (10,17,43,7,-1);
	paint_line (24,17,42,9,-1);
	paint_line (25,17,43,9,-1);
	paint_line (26,17,50,6,-1);
	paint_line (38,7,45,7,-1);
	//paint_line (30,9,37,9,-1);
	paint_point (42,8,-1);
	paint_point (43,8,-1);
	paint_point (41,8,-1);
	paint_point (40,8,-1);
	paint_line (22,14,31,14,-1);
	paint_line (25,13,34,13,-1);
	settextcolor(__WHITE, __WHITE);
	//paint_point (34,10,-1);
	paint_point (40,8,-1);
	settextcolor(__BLACK, __WHITE);
	
	// -
	//paint_line (22,8,48,8,-1);
	paint_line (32,6,58,6,-1);
	// <
	paint_line (36,13,68,6,-1);
	paint_line (37,13,69,6,-1);
	
	paint_line (44,11,68,17,-1);
	paint_line (45,11,69,17,-1);
	paint_point (38,12,-1);
	paint_point (39,12,-1);
	paint_point (45,12,-1);
	paint_point (46,12,-1);
	// Я
	settextcolor(__GREEN, __WHITE);
	paint_line (26,2,26,17,-1);
	paint_line (25,2,25,17,-1);
	paint_line (24,2,24,17,-1);
	paint_line (13,2,25,2,-1);
	paint_line (13,9,25,9,-1);
	
	//paint_point (5,4,-1);
	paint_line (12,3,13,3,-1);
	paint_line (12,8,13,8,-1);
	paint_line (11,4,12,4,-1);
	paint_line (11,7,12,7,-1);
	paint_line (10,5,11,5,-1);
	paint_line (10,6,11,6,-1);
	
	paint_line (9,17,20,9,-1);
	paint_line (10,17,21,9,-1);
	
	settextcolor(__BLACK, __WHITE);
	
	set_csr (29, 18);
	kprintf ("Yatak Operation System");
	set_csr (29, 19);
	kprintf ("Version: %s", YATAK_VERSION);
	set_csr (29, 20);
	kprintf ("Kernel version: %s",KERNEL_VERSION);
	set_csr (29, 21);
	kprintf ("Build: %s / %s",__DATE__,__TIME__);
#ifdef DEBUG_MAIN
	set_csr (29, 22);
	dprintf ("Compiled with gcc: %s",__VERSION__);
	set_csr (29, 23);
#else
	set_csr (29, 22);
#endif
#ifdef END_YEAR
	kprintf ("%s %s-%s by %s",COPYRIGTH, START_YEAR,END_YEAR, AUTHOR);
#else
	kprintf ("%s %s by %s",COPYRIGTH, START_YEAR, AUTHOR);
#endif
	set_csr (15, 24);
	kprintf ("Please, press any key to entry in the operating system");
	
	restore_flags (flags);
	in_char (true);
	cls ();
}